# main_wfo.py

import pandas as pd
import numpy as np
import os
from datetime import timedelta
from itertools import product
import matplotlib.pyplot as plt

# Import the backtester class and data loader from our main script
from main_backtest import DeltaCVDBotBacktester, load_data


def run_wfo(all_data, wfo_config, param_grid, costs):
    """
    Performs Walk-Forward Optimization.
    """
    training_days = wfo_config['training_days']
    testing_days = wfo_config['testing_days']

    start_date = all_data.index.min()
    end_date = all_data.index.max()

    all_wfo_trades = []
    current_date = start_date

    run_count = 1
    while current_date + timedelta(days=training_days + testing_days) <= end_date:
        # --- 1. Define Training and Testing Periods ---
        train_start = current_date
        train_end = current_date + timedelta(days=training_days)
        test_start = train_end
        test_end = test_start + timedelta(days=testing_days)

        training_df = all_data.loc[train_start:train_end]
        testing_df = all_data.loc[test_start:test_end]

        if training_df.empty or testing_df.empty:
            print(f"Skipping run {run_count} due to insufficient data in period.")
            current_date += timedelta(days=testing_days)  # Move to next window
            run_count += 1
            continue

        print(f"\n--- WFO Run #{run_count} ---")
        print(f"Training: {train_start.date()} to {train_end.date()}")
        print(f"Testing:  {test_start.date()} to {test_end.date()}")

        # --- 2. Optimization on Training Data ---
        best_params = None
        best_performance = -np.inf

        # Create all combinations of parameters
        param_combinations = [dict(zip(param_grid.keys(), v)) for v in product(*param_grid.values())]

        for params in param_combinations:
            backtester = DeltaCVDBotBacktester(training_df, params, costs)
            results = backtester.run_backtest()

            if results is not None and not results.empty:
                # We can optimize for different metrics, e.g., final PnL or Profit Factor
                performance = results['cumulative_pnl'].iloc[-1]
                if performance > best_performance:
                    best_performance = performance
                    best_params = params

        # --- 3. Test on Out-of-Sample Data ---
        if best_params:
            print(f"Best params found: {best_params} with performance {best_performance:.2%}")

            # Run the backtest on the testing data with the best parameters
            test_backtester = DeltaCVDBotBacktester(testing_df, best_params, costs)
            test_results = test_backtester.run_backtest()

            if test_results is not None and not test_results.empty:
                all_wfo_trades.append(test_results)
                print(f"Out-of-sample PnL: {test_results['cumulative_pnl'].iloc[-1]:.2%}")
        else:
            print("No profitable parameters found in training period.")

        # --- 4. Walk Forward ---
        current_date += timedelta(days=testing_days)
        run_count += 1

    return pd.concat(all_wfo_trades, ignore_index=True) if all_wfo_trades else None


if __name__ == '__main__':
    # --- Configuration ---
    SYMBOL = "ETHUSDT"
    TIMEFRAME = "1m"
    # Load multiple months for a meaningful WFO
    FILE_PATHS = [
        r"F:\personal\binance_klines_with_delta\ETHUSDT\1m\ETHUSDT-1m-2024-04.csv",
        r"F:\personal\binance_klines_with_delta\ETHUSDT\1m\ETHUSDT-1m-2024-05.csv",
        r"F:\personal\binance_klines_with_delta\ETHUSDT\1m\ETHUSDT-1m-2024-06.csv",
    ]

    # --- WFO Parameters ---
    wfo_config = {
        'training_days': 20,
        'testing_days': 10
    }

    # --- Parameter Grid for Optimization ---
    # Be careful, a large grid can be very slow! Start small.
    param_grid = {
        'divergence_lookback_period': [20, 30],
        'breakout_lookback_period': [20],
        'delta_ma_period': [20],
        'delta_strength_factor': [2.0, 2.5],
        'risk_reward_ratio': [1.5],
        'stop_loss_atr_multiplier': [2.0],
        'ema_trend_period': [50]
    }

    # --- Trading Costs ---
    trading_costs = {
        'taker_fee': 0.0005,
        'maker_fee': 0.0002,
        'slippage': 0.0001
    }

    # --- Load all data ---
    all_dfs = [load_data(path) for path in FILE_PATHS]
    full_data_df = pd.concat(all_dfs)

    # --- Run WFO ---
    wfo_results_df = run_wfo(full_data_df, wfo_config, param_grid, trading_costs)

    # --- Analyze WFO Results ---
    if wfo_results_df is not None and not wfo_results_df.empty:
        print("\n\n--- Overall WFO Performance ---")

        # To plot a continuous equity curve, we need to chain the PnL
        wfo_results_df = wfo_results_df.sort_values(by='exit_time').reset_index(drop=True)
        wfo_results_df['cumulative_pnl'] = (1 + wfo_results_df['pnl_percent']).cumprod() - 1

        # Use the stats printer from the main backtester class (can be refactored to be a static method)
        dummy_backtester = DeltaCVDBotBacktester(pd.DataFrame(), {}, {})
        dummy_backtester.print_stats(wfo_results_df)
        dummy_backtester.plot_results(wfo_results_df)
    else:
        print("WFO did not produce any trades.")